/*
 * ! Ext JS Library 3.1.1 Copyright(c) 2006-2010 Ext JS, LLC licensing@extjs.com
 * http://www.extjs.com/license
 */
Ext.ns('Ext.ux.grid');

Ext.ux.grid.LockingGridView = Ext.extend(Ext.grid.GridView, {
  rowBorderWidth : 1,
  lockedBorderWidth : 1,
  /*
   * This option ensures that height between the rows is synchronized between
   * the locked and unlocked sides. This option only needs to be used when the
   * row heights isn't predictable.
   */
  syncHeights : false,
  initTemplates : function() {
    var ts = this.templates || {};
    if (!ts.master) {
      ts.master = new Ext.Template(
          '<div class="x-grid3" hidefocus="true">',
          '<div class="x-grid3-locked">',
          '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{lstyle}">{lockedHeader}</div></div><div class="x-clear"></div></div>',
          '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',
          '</div>',
          '<div class="x-grid3-viewport x-grid3-unlocked">',
          '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
          '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
          '</div>', '<div class="x-grid3-resize-marker">&#160;</div>',
          '<div class="x-grid3-resize-proxy">&#160;</div>', '</div>');
    }
    this.templates = ts;
    Ext.ux.grid.LockingGridView.superclass.initTemplates.call(this);
  },
  getEditorParent : function(ed) {
    return this.el.dom;
  },
  initElements : function() {
    var E = Ext.Element;
    var el = this.grid.getGridEl().dom.firstChild;
    var cs = el.childNodes;
    this.el = new E(el);
    this.lockedWrap = new E(cs[0]);
    this.lockedHd = new E(this.lockedWrap.dom.firstChild);
    this.lockedInnerHd = this.lockedHd.dom.firstChild;
    this.lockedScroller = new E(this.lockedWrap.dom.childNodes[1]);
    this.lockedBody = new E(this.lockedScroller.dom.firstChild);
    this.mainWrap = new E(cs[1]);
    this.mainHd = new E(this.mainWrap.dom.firstChild);
    if (this.grid.hideHeaders) {
      this.lockedHd.setDisplayed(false);
      this.mainHd.setDisplayed(false);
    }
    this.innerHd = this.mainHd.dom.firstChild;
    this.scroller = new E(this.mainWrap.dom.childNodes[1]);
    if (this.forceFit) {
      this.scroller.setStyle('overflow-x', 'hidden');
    }
    this.mainBody = new E(this.scroller.dom.firstChild);
    this.focusEl = new E(this.scroller.dom.childNodes[1]);
    this.focusEl.swallowEvent('click', true);
    this.resizeMarker = new E(cs[2]);
    this.resizeProxy = new E(cs[3]);
  },

  getLockedRows : function() {
    return this.hasRows() ? this.lockedBody.dom.childNodes : [];
  },

  getLockedRow : function(row) {
    return this.getLockedRows()[row];
  },

  getCell : function(row, col) {
    var llen = this.cm.getLockedCount();
    if (col < llen) {
      return this.getLockedRow(row).getElementsByTagName('td')[col];
    }
    return Ext.ux.grid.LockingGridView.superclass.getCell.call(this, row, col - llen);
  },

  getHeaderCell : function(index) {
    var llen = this.cm.getLockedCount();
    if (index < llen) {
      return this.lockedHd.dom.getElementsByTagName('td')[index];
    }
    return Ext.ux.grid.LockingGridView.superclass.getHeaderCell.call(this, index - llen);
  },

  addRowClass : function(row, cls) {
    var r = this.getLockedRow(row);
    if (r) {
      this.fly(r).addClass(cls);
    }
    Ext.ux.grid.LockingGridView.superclass.addRowClass.call(this, row, cls);
  },

  removeRowClass : function(row, cls) {
    var r = this.getLockedRow(row);
    if (r) {
      this.fly(r).removeClass(cls);
    }
    Ext.ux.grid.LockingGridView.superclass.removeRowClass.call(this, row, cls);
  },

  removeRow : function(row) {
    Ext.removeNode(this.getLockedRow(row));
    Ext.ux.grid.LockingGridView.superclass.removeRow.call(this, row);
  },

  removeRows : function(firstRow, lastRow) {
    var bd = this.lockedBody.dom;
    for (var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++) {
      Ext.removeNode(bd.childNodes[firstRow]);
    }
    Ext.ux.grid.LockingGridView.superclass.removeRows.call(this, firstRow, lastRow);
  },

  syncScroll : function(e) {
    var mb = this.scroller.dom;
    this.lockedScroller.dom.scrollTop = mb.scrollTop;
    Ext.ux.grid.LockingGridView.superclass.syncScroll.call(this, e);
  },

  updateSortIcon : function(col, dir) {
    var sc = this.sortClasses, lhds = this.lockedHd.select('td').removeClass(sc), hds = this.mainHd
        .select('td').removeClass(sc), llen = this.cm.getLockedCount(), cls = sc[dir == 'DESC'
        ? 1
        : 0];
    if (col < llen) {
      lhds.item(col).addClass(cls);
    } else {
      hds.item(col - llen).addClass(cls);
    }
  },

  updateAllColumnWidths : function() {
    var tw = this.getTotalWidth(), clen = this.cm.getColumnCount(), lw = this.getLockedWidth(), llen = this.cm
        .getLockedCount(), ws = [], len, i;
    this.updateLockedWidth();
    for (i = 0; i < clen; i++) {
      ws[i] = this.getColumnWidth(i);
      var hd = this.getHeaderCell(i);
      hd.style.width = ws[i];
    }
    var lns = this.getLockedRows(), ns = this.getRows(), row, trow, j;
    for (i = 0, len = ns.length; i < len; i++) {
      row = lns[i];
      row.style.width = lw;
      if (row.firstChild) {
        row.firstChild.style.width = lw;
        trow = row.firstChild.rows[0];
        for (j = 0; j < llen; j++) {
          trow.childNodes[j].style.width = ws[j];
        }
      }
      row = ns[i];
      row.style.width = tw;
      if (row.firstChild) {
        row.firstChild.style.width = tw;
        trow = row.firstChild.rows[0];
        for (j = llen; j < clen; j++) {
          trow.childNodes[j - llen].style.width = ws[j];
        }
      }
    }
    this.onAllColumnWidthsUpdated(ws, tw);
    this.syncHeaderHeight();
  },

  updateColumnWidth : function(col, width) {
    var w = this.getColumnWidth(col), llen = this.cm.getLockedCount(), ns, rw, c, row;
    this.updateLockedWidth();
    if (col < llen) {
      ns = this.getLockedRows();
      rw = this.getLockedWidth();
      c = col;
    } else {
      ns = this.getRows();
      rw = this.getTotalWidth();
      c = col - llen;
    }
    var hd = this.getHeaderCell(col);
    hd.style.width = w;
    for (var i = 0, len = ns.length; i < len; i++) {
      row = ns[i];
      row.style.width = rw;
      if (row.firstChild) {
        row.firstChild.style.width = rw;
        row.firstChild.rows[0].childNodes[c].style.width = w;
      }
    }
    this.onColumnWidthUpdated(col, w, this.getTotalWidth());
    this.syncHeaderHeight();
  },

  updateColumnHidden : function(col, hidden) {
    var llen = this.cm.getLockedCount(), ns, rw, c, row, display = hidden ? 'none' : '';
    this.updateLockedWidth();
    if (col < llen) {
      ns = this.getLockedRows();
      rw = this.getLockedWidth();
      c = col;
    } else {
      ns = this.getRows();
      rw = this.getTotalWidth();
      c = col - llen;
    }
    var hd = this.getHeaderCell(col);
    hd.style.display = display;
    for (var i = 0, len = ns.length; i < len; i++) {
      row = ns[i];
      row.style.width = rw;
      if (row.firstChild) {
        row.firstChild.style.width = rw;
        row.firstChild.rows[0].childNodes[c].style.display = display;
      }
    }
    this.onColumnHiddenUpdated(col, hidden, this.getTotalWidth());
    delete this.lastViewWidth;
    this.layout();
  },

  doRender : function(cs, rs, ds, startRow, colCount, stripe) {
    var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount - 1, tstyle = 'width:'
        + this.getTotalWidth() + ';', lstyle = 'width:' + this.getLockedWidth() + ';', buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r;
    for (var j = 0, len = rs.length; j < len; j++) {
      r = rs[j];
      cb = [];
      lcb = [];
      var rowIndex = (j + startRow);
      for (var i = 0; i < colCount; i++) {
        c = cs[i];
        p.id = c.id;
        p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : ''))
            + (this.cm.config[i].cellCls ? ' ' + this.cm.config[i].cellCls : '');
        p.attr = p.cellAttr = '';
        p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
        p.style = c.style;
        if (Ext.isEmpty(p.value)) {
          p.value = '&#160;';
        }
        if (this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])) {
          p.css += ' x-grid3-dirty-cell';
        }
        if (c.locked) {
          lcb[lcb.length] = ct.apply(p);
        } else {
          cb[cb.length] = ct.apply(p);
        }
      }
      var alt = [];
      if (stripe && ((rowIndex + 1) % 2 === 0)) {
        alt[0] = 'x-grid3-row-alt';
      }
      if (r.dirty) {
        alt[1] = ' x-grid3-dirty-row';
      }
      rp.cols = colCount;
      if (this.getRowClass) {
        alt[2] = this.getRowClass(r, rowIndex, rp, ds);
      }
      rp.alt = alt.join(' ');
      rp.cells = cb.join('');
      rp.tstyle = tstyle;
      buf[buf.length] = rt.apply(rp);
      rp.cells = lcb.join('');
      rp.tstyle = lstyle;
      lbuf[lbuf.length] = rt.apply(rp);
    }
    return [buf.join(''), lbuf.join('')];
  },
  processRows : function(startRow, skipStripe) {
    if (!this.ds || this.ds.getCount() < 1) {
      return;
    }
    var rows = this.getRows(), lrows = this.getLockedRows(), row, lrow;
    skipStripe = skipStripe || !this.grid.stripeRows;
    startRow = startRow || 0;
    for (var i = 0, len = rows.length; i < len; ++i) {
      row = rows[i];
      lrow = lrows[i];
      row.rowIndex = i;
      lrow.rowIndex = i;
      if (!skipStripe) {
        row.className = row.className.replace(this.rowClsRe, ' ');
        lrow.className = lrow.className.replace(this.rowClsRe, ' ');
        if ((i + 1) % 2 === 0) {
          row.className += ' x-grid3-row-alt';
          lrow.className += ' x-grid3-row-alt';
        }
      }
      if (this.syncHeights) {
        var el1 = Ext.get(row), el2 = Ext.get(lrow), h1 = el1.getHeight(), h2 = el2.getHeight();

        if (h1 > h2) {
          el2.setHeight(h1);
        } else if (h2 > h1) {
          el1.setHeight(h2);
        }
      }
    }
    if (startRow === 0) {
      Ext.fly(rows[0]).addClass(this.firstRowCls);
      Ext.fly(lrows[0]).addClass(this.firstRowCls);
    }
    Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
    Ext.fly(lrows[lrows.length - 1]).addClass(this.lastRowCls);
  },

  afterRender : function() {
    if (!this.ds || !this.cm) {
      return;
    }
    var bd = this.renderRows() || ['&#160;', '&#160;'];
    this.mainBody.dom.innerHTML = bd[0];
    this.lockedBody.dom.innerHTML = bd[1];
    this.processRows(0, true);
    if (this.deferEmptyText !== true) {
      this.applyEmptyText();
    }
  },

  renderUI : function() {
    var header = this.renderHeaders();
    var body = this.templates.body.apply({
          rows : '&#160;'
        });
    var html = this.templates.master.apply({
          body : body,
          header : header[0],
          ostyle : 'width:' + this.getOffsetWidth() + ';',
          bstyle : 'width:' + this.getTotalWidth() + ';',
          lockedBody : body,
          lockedHeader : header[1],
          lstyle : 'width:' + this.getLockedWidth() + ';'
        });
    var g = this.grid;
    g.getGridEl().dom.innerHTML = html;
    this.initElements();
    Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
    Ext.fly(this.lockedInnerHd).on('click', this.handleHdDown, this);
    this.mainHd.on({
          scope : this,
          mouseover : this.handleHdOver,
          mouseout : this.handleHdOut,
          mousemove : this.handleHdMove
        });
    this.lockedHd.on({
          scope : this,
          mouseover : this.handleHdOver,
          mouseout : this.handleHdOut,
          mousemove : this.handleHdMove
        });
    this.scroller.on('scroll', this.syncScroll, this);
    if (g.enableColumnResize !== false) {
      this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
      this.splitZone.setOuterHandleElId(Ext.id(this.lockedHd.dom));
      this.splitZone.setOuterHandleElId(Ext.id(this.mainHd.dom));
    }
    if (g.enableColumnMove) {
      this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
      this.columnDrag.setOuterHandleElId(Ext.id(this.lockedInnerHd));
      this.columnDrag.setOuterHandleElId(Ext.id(this.innerHd));
      this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
    }
    if (g.enableHdMenu !== false) {
      this.hmenu = new Ext.menu.Menu({
            id : g.id + '-hctx'
          });
      this.hmenu.add({
            itemId : 'asc',
            text : this.sortAscText,
            cls : 'xg-hmenu-sort-asc'
          }, {
            itemId : 'desc',
            text : this.sortDescText,
            cls : 'xg-hmenu-sort-desc'
          });
      if (this.grid.enableColLock !== false) {
        this.hmenu.add('-', {
              itemId : 'lock',
              text : this.lockText,
              cls : 'xg-hmenu-lock'
            }, {
              itemId : 'unlock',
              text : this.unlockText,
              cls : 'xg-hmenu-unlock'
            });
      }
      if (g.enableColumnHide !== false) {
        this.colMenu = new Ext.menu.Menu({
              id : g.id + '-hcols-menu'
            });
        this.colMenu.on({
              scope : this,
              beforeshow : this.beforeColMenuShow,
              itemclick : this.handleHdMenuClick
            });
        this.hmenu.add('-', {
              itemId : 'columns',
              hideOnClick : false,
              text : this.columnsText,
              menu : this.colMenu,
              iconCls : 'x-cols-icon'
            });
      }
      this.hmenu.on('itemclick', this.handleHdMenuClick, this);
    }
    if (g.trackMouseOver) {
      this.mainBody.on({
            scope : this,
            mouseover : this.onRowOver,
            mouseout : this.onRowOut
          });
      this.lockedBody.on({
            scope : this,
            mouseover : this.onRowOver,
            mouseout : this.onRowOut
          });
    }

    if (g.enableDragDrop || g.enableDrag) {
      this.dragZone = new Ext.grid.GridDragZone(g, {
            ddGroup : g.ddGroup || 'GridDD'
          });
    }
    this.updateHeaderSortState();
  },

  layout : function() {
    if (!this.mainBody) {
      return;
    }
    var g = this.grid;
    var c = g.getGridEl();
    var csize = c.getSize(true);
    var vw = csize.width;
    if (!g.hideHeaders && (vw < 20 || csize.height < 20)) {
      return;
    }
    this.syncHeaderHeight();
    if (g.autoHeight) {
      this.scroller.dom.style.overflow = 'visible';
      this.lockedScroller.dom.style.overflow = 'visible';
      if (Ext.isWebKit) {
        this.scroller.dom.style.position = 'static';
        this.lockedScroller.dom.style.position = 'static';
      }
    } else {
      this.el.setSize(csize.width, csize.height);
      var hdHeight = this.mainHd.getHeight();
      var vh = csize.height - (hdHeight);
    }
    this.updateLockedWidth();
    if (this.forceFit) {
      if (this.lastViewWidth != vw) {
        this.fitColumns(false, false);
        this.lastViewWidth = vw;
      }
    } else {
      this.autoExpand();
      this.syncHeaderScroll();
    }
    this.onLayout(vw, vh);
  },

  getOffsetWidth : function() {
    return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth() + this.getScrollOffset())
        + 'px';
  },

  renderHeaders : function() {
    var cm = this.cm, ts = this.templates, ct = ts.hcell, cb = [], lcb = [], p = {}, len = cm
        .getColumnCount(), last = len - 1;
    for (var i = 0; i < len; i++) {
      p.id = cm.getColumnId(i);
      p.value = cm.getColumnHeader(i) || '';
      p.style = this.getColumnStyle(i, true);
      p.tooltip = this.getColumnTooltip(i);
      p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : ''))
          + (cm.config[i].headerCls ? ' ' + cm.config[i].headerCls : '');
      if (cm.config[i].align == 'right') {
        p.istyle = 'padding-right:16px';
      } else {
        delete p.istyle;
      }
      if (cm.isLocked(i)) {
        lcb[lcb.length] = ct.apply(p);
      } else {
        cb[cb.length] = ct.apply(p);
      }
    }
    return [ts.header.apply({
              cells : cb.join(''),
              tstyle : 'width:' + this.getTotalWidth() + ';'
            }), ts.header.apply({
              cells : lcb.join(''),
              tstyle : 'width:' + this.getLockedWidth() + ';'
            })];
  },

  updateHeaders : function() {
    var hd = this.renderHeaders();
    this.innerHd.firstChild.innerHTML = hd[0];
    this.innerHd.firstChild.style.width = this.getOffsetWidth();
    this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
    this.lockedInnerHd.firstChild.innerHTML = hd[1];
    var lw = this.getLockedWidth();
    this.lockedInnerHd.firstChild.style.width = lw;
    this.lockedInnerHd.firstChild.firstChild.style.width = lw;
  },

  getResolvedXY : function(resolved) {
    if (!resolved) {
      return null;
    }
    var c = resolved.cell, r = resolved.row;
    return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];
  },

  syncFocusEl : function(row, col, hscroll) {
    Ext.ux.grid.LockingGridView.superclass.syncFocusEl.call(this, row, col, col < this.cm
            .getLockedCount() ? false : hscroll);
  },

  ensureVisible : function(row, col, hscroll) {
    return Ext.ux.grid.LockingGridView.superclass.ensureVisible.call(this, row, col, col < this.cm
            .getLockedCount() ? false : hscroll);
  },

  insertRows : function(dm, firstRow, lastRow, isUpdate) {
    var last = dm.getCount() - 1;
    if (!isUpdate && firstRow === 0 && lastRow >= last) {
      this.refresh();
    } else {
      if (!isUpdate) {
        this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
      }
      var html = this.renderRows(firstRow, lastRow), before = this.getRow(firstRow);
      if (before) {
        if (firstRow === 0) {
          this.removeRowClass(0, this.firstRowCls);
        }
        Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);
        before = this.getLockedRow(firstRow);
        Ext.DomHelper.insertHtml('beforeBegin', before, html[1]);
      } else {
        this.removeRowClass(last - 1, this.lastRowCls);
        Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html[0]);
        Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom, html[1]);
      }
      if (!isUpdate) {
        this.fireEvent('rowsinserted', this, firstRow, lastRow);
        this.processRows(firstRow);
      } else if (firstRow === 0 || firstRow >= last) {
        this.addRowClass(firstRow, firstRow === 0 ? this.firstRowCls : this.lastRowCls);
      }
    }
    this.syncFocusEl(firstRow);
  },

  getColumnStyle : function(col, isHeader) {
    var style = !isHeader
        ? this.cm.config[col].cellStyle || this.cm.config[col].css || ''
        : this.cm.config[col].headerStyle || '';
    style += 'width:' + this.getColumnWidth(col) + ';';
    if (this.cm.isHidden(col)) {
      style += 'display:none;';
    }
    var align = this.cm.config[col].align;
    if (align) {
      style += 'text-align:' + align + ';';
    }
    return style;
  },

  getLockedWidth : function() {
    return this.cm.getTotalLockedWidth() + 'px';
  },

  getTotalWidth : function() {
    return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth()) + 'px';
  },

  getColumnData : function() {
    var cs = [], cm = this.cm, colCount = cm.getColumnCount();
    for (var i = 0; i < colCount; i++) {
      var name = cm.getDataIndex(i);
      cs[i] = {
        name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
        renderer : cm.getRenderer(i),
        id : cm.getColumnId(i),
        style : this.getColumnStyle(i),
        locked : cm.isLocked(i)
      };
    }
    return cs;
  },

  renderBody : function() {
    var markup = this.renderRows() || ['&#160;', '&#160;'];
    return [this.templates.body.apply({
              rows : markup[0]
            }), this.templates.body.apply({
              rows : markup[1]
            })];
  },

  refreshRow : function(record) {
    Ext.ux.grid.LockingGridView.superclass.refreshRow.call(this, record);
    var index = Ext.isNumber(record) ? record : this.ds.indexOf(record);
    this.getLockedRow(index).rowIndex = index;
  },

  refresh : function(headersToo) {
    this.fireEvent('beforerefresh', this);
    this.grid.stopEditing(true);
    var result = this.renderBody();
    this.mainBody.update(result[0]).setWidth(this.getTotalWidth());
    this.lockedBody.update(result[1]).setWidth(this.getLockedWidth());
    if (headersToo === true) {
      this.updateHeaders();
      this.updateHeaderSortState();
    }
    this.processRows(0, true);
    this.layout();
    this.applyEmptyText();
    this.fireEvent('refresh', this);
  },

  onDenyColumnLock : function() {

  },

  initData : function(ds, cm) {
    if (this.cm) {
      this.cm.un('columnlockchange', this.onColumnLock, this);
    }
    Ext.ux.grid.LockingGridView.superclass.initData.call(this, ds, cm);
    if (this.cm) {
      this.cm.on('columnlockchange', this.onColumnLock, this);
    }
  },

  onColumnLock : function() {
    this.refresh(true);
  },

  handleHdMenuClick : function(item) {
    var index = this.hdCtxIndex, cm = this.cm, id = item.getItemId(), llen = cm.getLockedCount();
    switch (id) {
      case 'lock' :
        if (cm.getColumnCount(true) <= llen + 1) {
          this.onDenyColumnLock();
          return;
        }
        if (llen != index) {
          cm.setLocked(index, true, true);
          cm.moveColumn(index, llen);
          this.grid.fireEvent('columnmove', index, llen);
        } else {
          cm.setLocked(index, true);
        }
        break;
      case 'unlock' :
        if (llen - 1 != index) {
          cm.setLocked(index, false, true);
          cm.moveColumn(index, llen - 1);
          this.grid.fireEvent('columnmove', index, llen - 1);
        } else {
          cm.setLocked(index, false);
        }
        break;
      default :
        return Ext.ux.grid.LockingGridView.superclass.handleHdMenuClick.call(this, item);
    }
    return true;
  },

  handleHdDown : function(e, t) {
    Ext.ux.grid.LockingGridView.superclass.handleHdDown.call(this, e, t);
    if (this.grid.enableColLock !== false) {
      if (Ext.fly(t).hasClass('x-grid3-hd-btn')) {
        var hd = this.findHeaderCell(t), index = this.getCellIndex(hd), ms = this.hmenu.items, cm = this.cm;
        ms.get('lock').setDisabled(cm.isLocked(index));
        ms.get('unlock').setDisabled(!cm.isLocked(index));
      }
    }
  },

  syncHeaderHeight : function() {
    this.innerHd.firstChild.firstChild.style.height = 'auto';
    this.lockedInnerHd.firstChild.firstChild.style.height = 'auto';
    var hd = this.innerHd.firstChild.firstChild.offsetHeight, lhd = this.lockedInnerHd.firstChild.firstChild.offsetHeight, height = (lhd > hd
        ? lhd
        : hd)
        + 'px';
    this.innerHd.firstChild.firstChild.style.height = height;
    this.lockedInnerHd.firstChild.firstChild.style.height = height;
  },

  updateLockedWidth : function() {
    var lw = this.cm.getTotalLockedWidth(), tw = this.cm.getTotalWidth() - lw, csize = this.grid
        .getGridEl().getSize(true), lp = Ext.isBorderBox ? 0 : this.lockedBorderWidth, rp = Ext.isBorderBox
        ? 0
        : this.rowBorderWidth, vw = (csize.width - lw - lp - rp) + 'px', so = this
        .getScrollOffset();
    if (!this.grid.autoHeight) {
      var vh = (csize.height - this.mainHd.getHeight()) + 'px';
      this.lockedScroller.dom.style.height = vh;
      this.scroller.dom.style.height = vh;
    }
    this.lockedWrap.dom.style.width = (lw + rp) + 'px';
    this.scroller.dom.style.width = vw;
    this.mainWrap.dom.style.left = (lw + lp + rp) + 'px';
    if (this.innerHd) {
      this.lockedInnerHd.firstChild.style.width = lw + 'px';
      this.lockedInnerHd.firstChild.firstChild.style.width = lw + 'px';
      this.innerHd.style.width = vw;
      this.innerHd.firstChild.style.width = (tw + rp + so) + 'px';
      this.innerHd.firstChild.firstChild.style.width = tw + 'px';
    }
    if (this.mainBody) {
      this.lockedBody.dom.style.width = (lw + rp) + 'px';
      this.mainBody.dom.style.width = (tw + rp) + 'px';
    }
  }
});

Ext.ux.grid.LockingColumnModel = Ext.extend(Ext.grid.ColumnModel, {
      isLocked : function(colIndex) {
        return this.config[colIndex].locked === true;
      },

      setLocked : function(colIndex, value, suppressEvent) {
        if (this.isLocked(colIndex) == value) {
          return;
        }
        this.config[colIndex].locked = value;
        if (!suppressEvent) {
          this.fireEvent('columnlockchange', this, colIndex, value);
        }
      },

      getTotalLockedWidth : function() {
        var totalWidth = 0;
        for (var i = 0, len = this.config.length; i < len; i++) {
          if (this.isLocked(i) && !this.isHidden(i)) {
            totalWidth += this.getColumnWidth(i);
          }
        }
        return totalWidth;
      },

      getLockedCount : function() {
        for (var i = 0, len = this.config.length; i < len; i++) {
          if (!this.isLocked(i)) {
            return i;
          }
        }
      },

      moveColumn : function(oldIndex, newIndex) {
        if (oldIndex < newIndex && this.isLocked(oldIndex) && !this.isLocked(newIndex)) {
          this.setLocked(oldIndex, false, true);
        } else if (oldIndex > newIndex && !this.isLocked(oldIndex) && this.isLocked(newIndex)) {
          this.setLocked(oldIndex, true, true);
        }
        Ext.ux.grid.LockingColumnModel.superclass.moveColumn.apply(this, arguments);
      }
    });
